38
```
在浏览器中查看首页时,你会看到新添加的“个人中心”链接已经出现在了侧边栏中。
### Xiuno BBS的路由机制
我相信你可能看不懂刚才写的内容,所以我们来直接观察网址:
```
localhost/route-action.htm
```
其中的“route”会在`index.inc.php`里进行初次分化(可以通过`param(0)`获取到)。
- 具体点说,会在`switch ($route)`里的各个case来决定include哪个route的php文件。我们很快就会谈到这个。
- 在route文件中,使用`param(1)`来获取到“action”
- param函数具体是什么将会在后文中提到。
```
localhost/route-action-subaction.htm
```
- 使用`param(2)`来获取到“subaction”
你应该看出了规律。param函数的第一个参数是可以输入数字的,它将对应网址中的第几个“-”前面的内容,例如:
```
localhost/a-b-c-d-e.htm?some_data=123
```
会这样对应:
- `param(0)` → a
- `param(1)` → b
- `param(2)` → c
- `param(3)` → d
- `param(4)` → e
- `param('some_data')` → 123
那么你可以这样写PHP来制作你自己的路由:
```php
```
以上例子值得借鉴。
### 如何给用户提供正确的链接
使用`url()`函数来保证输出的链接是准确的,随着伪静态功能开关而变的:
```php
```
这样会输出:
```
index.php?route-action.htm //关闭伪静态
route-action.htm //开启伪静态
```
该函数最常见的用途是:
```php
点击
= $_thread['subject'] ?>
```
此处注意:要在HTML里写`url()`,要注意**写单引号**而不是双引号,用点来连接不同部分,这样在你选择“格式化代码”的时候,不会把url函数格式搞乱(通常是多出来一个空格),继而导致链接失效。
> 虽然原装的Xiuno BBS就是用双引号语法`
">`但不代表我们也应该这样写。
url()函数的第二个参数可以输入数组,来传递参数:
```php
123, 'other_data' => 456));?>
```
则会输出类似:
```
index.php?route-action.htm&some_data=123&other_data=456 //关闭伪静态
route-action.htm?some_data=123&other_data=456 //开启伪静态
```
注意some\_data左边的符号,会根据伪静态开关而改变是问号还是与号。
### 如何给用户提供可以输入内容的表单控件
你可以直接写HTML,但Xiuno BBS提供了更方便的函数:
```php
/**
* 生成是/否单选按钮组
*
* 比复选框更容易理解,又有类似的作用
*
* @param string $name 表单元素名称
* @param int $checked 默认选中值
* @return string HTML字符串
*/
function form_radio_yes_no($name, $checked = 0) : string
/**
* 生成单选按钮组
* @param string $name 表单元素名称
* @param array $arr 选项数组,其中键为实际的value,值是显示文字
* @param mixed $checked 默认选中值
* @return string HTML字符串
*/
function form_radio($name, $arr, $checked = 0) : string
/**
* 生成复选框
* @param string $name 表单元素名称
* @param int $checked 是否选中
* @param string $txt 显示文本
* @return string HTML字符串
*/
function form_checkbox($name, $checked = 0, $txt = '') : string
/**
* 生成多个复选框
* @param string $name 表单元素名称
* @param array $arr 选项数组,其中键为实际的value,值是显示文字
* @param array $checked 默认选中值数组
* @return string HTML字符串
*/
function form_multi_checkbox($name, $arr, $checked = array()) : string
/**
* 生成下拉选择框
* @param string $name 表单元素名称
* @param array $arr 选项数组,其中键为实际的value,值是显示文字
* @param mixed $checked 默认选中值
* @param mixed $id ID属性
* @return string HTML字符串
*/
function form_select($name, $arr, $checked = 0, $id = TRUE) : string
/**
* 生成选项列表
*
* 是form_select附属的函数,不建议直接使用
*
* @param array $arr 选项数组
* @param mixed $checked 默认选中值
* @return string HTML字符串
*/
function form_options($arr, $checked = 0) : string
/**
* 生成单行文本输入框
* @param string $name 表单元素名称
* @param string $value 默认值
* @param string|bool $width 宽度:如果是字符串,则设置为对应的值(应为CSS可用的单位,含单位,如100px),如果不输入(默认值false)则不设置
* @param string $holdplacer 占位符文本;是的,这个形参是holdplacer而不是placeholder
* @return string HTML字符串
*/
function form_text($name, $value, $width = FALSE, $holdplacer = '') : string
/**
* 生成隐藏输入框
*
* 用于在表单中添加额外的数据,例如用户ID。隐藏输入框的值不会显示,但仍然会提交到服务器,如果你不会在JS里组装请求数据,那么隐藏输入框就很有用。
*
* @param string $name 表单元素名称
* @param string $value 值
* @return string HTML字符串
*/
function form_hidden($name, $value) : string
/**
* 生成多行文本框
* @param string $name 表单元素名称
* @param string $value 默认值
* @param string|bool $width 宽度:如果是字符串,则设置为对应的值(应为CSS可用的单位,含单位,如100px),如果不输入(默认值false)则不设置
* @param string|bool $height 高度:如果是字符串,则设置为对应的值(应为CSS可用的单位,含单位,如100px),如果不输入(默认值false)则不设置
* @return string HTML字符串
*/
function form_textarea($name, $value, $width = FALSE, $height = FALSE) : string
/**
* 生成密码输入框
* @param string $name 表单元素名称
* @param string $value 默认值
* @param string|bool $width 宽度:如果是字符串,则设置为对应的值(应为CSS可用的单位,含单位,如100px),如果不输入(默认值false)则不设置
* @return string HTML字符串
*/
function form_password($name, $value, $width = FALSE) : string
```
以上函数都能用echo输出Bootstrap 4风格输入框,也可以直接赋值给变量然后再echo。
最常见的使用情景是在后台插件设置中,但实际上在前台页面也能用。只是这些函数的可控性低(如不能加自己的class来实现特定外观效果),所以直接写HTML input更好。
### 获取用户输入内容
在Xiuno BBS中,获取用户输入内容的**最好**方式是使用`param()`函数:
```php
$username = param('username',''); // 获取GET或POST的username参数
```
#### 为什么最好要用param函数?
1. 该函数默认会对字符串值应用`htmlspecialchars`函数,可以**有效防止跨站脚本攻击(XSS)**。当用户输入包含恶意脚本时,这些脚本会被转义,从而失去执行能力。
2. 当请求中没有特定的参数时,该函数可以指定默认值,**避免了因变量未定义而导致的错误。**
3. 该函数可以根据提供的默认值**自动进行类型转换**,例如,如果默认值是整数,那么即使用户提交的是字符串,`param`函数也会尝试将其转换为整数。
4. 该函数从`$_REQUEST`超全局变量中获取数据,这意味着它可以同时处理GET、POST和其他HTTP请求方法传递的参数,不用思考该用哪个超全局变量。
注意:\*\*param函数不适用于处理文件上传,\*\*因为文件上传的数据存储在`$_FILES`超全局变量中,而`$_FILES`并不包含在`$_REQUEST`中。对于文件上传,你需要直接访问`$_FILES`数组来获取上传文件的相关信息。
#### 函数签名
[看不懂的话,请点击这里查看《PHP手册》里有关函数原型的解释](https://www.php.net/manual/zh/about.prototypes.php)
```php
function param(int|string $key, mixed $defval, bool $htmlspecialchars = TRUE, bool $addslashes = FALSE): mixed
```
#### 参数说明
- **$key** (int|string): 需要获取的请求参数的名称。这是必填参数。
- **$defval** (mixed, 可选): 如果请求中不存在 `$key` 参数,则返回的默认值。
- **$htmlspecialchars** (bool, 可选): 是否对字符串值应用 `htmlspecialchars` 函数,防止XSS攻击。默认为 `TRUE`。
- **$addslashes** (bool, 可选): 是否对字符串值应用 `addslashes` 函数,防止SQL注入。默认为 `FALSE`。
#### 返回值
- 根据 `$key` 获取到的值,并转换成`$defval`参数的数据类型。
- 若未指定`$defval`,则直接返回 `$_REQUEST[$key]`的值和应有的数据类型。
- 如果该值不存在或为空,则返回 `$defval` 定义的默认值。
#### 函数内部逻辑
1. 检查请求参数是否存在:
- 如果请求参数 `$key` 不存在或其值为空,函数将直接返回 `$defval`。
2. 获取请求参数值:
- 从 `$_REQUEST` 中获取 `$key` 对应的值 `$val`。
3. 处理参数值:
- 如果 `$defval` 是数组,会进行特殊的处理,包括递归处理数组中的每个元素。
- 对于非数组的值,根据 `$defval` 的类型和提供的选项(`$htmlspecialchars` 和 `$addslashes`)来处理 `$val`,确保输出的安全性。
- 如果 `$defval` 是字符串类型,会对 `$val` 进行 `addslashes` 或 `htmlspecialchars` 处理,具体取决于提供的选项。
#### 示例
1. 基本用法:
```php
$value = param('test');
```
- 如果 `$_REQUEST['test']` 存在且不为空,返回其值。
- 如果 `$_REQUEST['test']` 不存在或为空,返回NULL。
2. 指定默认值:
```php
$value = param('test', '');
```
- 如果 `$_REQUEST['test']` 存在且不为空,返回其值。
- 如果 `$_REQUEST['test']` 不存在或为空,返回空字符串。
3. 强制转换为整型:
```php
$value = param('test', 0);
```
- 如果 `$_REQUEST['test']` 存在且不为空,返回其值并强制转换为整型。
- 如果 `$_REQUEST['test']` 不存在或为空,返回0。
4. 强制转换为数组:
```php
$value = param('test', array());
```
- 如果 `$_REQUEST['test']` 存在且不为空,返回其值并强制转换为数组。
- 如果 `$_REQUEST['test']` 不存在或为空,返回空数组。
5. 数组元素强制转换为字符串:
```php
$value = param('test', array(''));
```
- 如果 `$_REQUEST['test']` 存在且不为空,返回其值并强制转换为数组,且数组中的每个元素都强制转换为字符串。
- 如果 `$_REQUEST['test']` 不存在或为空,返回包含一个空字符串的数组 。
6. 数组元素强制转换为整型:
```php
$value = param('test', array(0));
```
- 如果 `$_REQUEST['test']` 存在且不为空,返回其值并强制转换为数组,且数组中的每个元素都强制转换为整型。
- 如果 `$_REQUEST['test']` 不存在或为空,返回包含一个整型0的数组 。
7. 完整用法:
```php
$username = param('username', '');
$age = param('age', 0);
$hobbies = param('hobbies', array());
$colors = param('colors', array(''));
$numbers = param('numbers', array(0));
```
当请求参数是:`?username=admin&age=18&hobbies[]=coding&hobbies[]=42&colors[]=%E7%BA%A2%E8%89%B2&numbers[]=10&&numbers[]=20` 时,以上代码将分别返回:
- admin
- 18
- \['coding', 42]
- \['红色']
- \[10, 20]
### 怎样存储你的数据
有很多种选择。
对于新手而言,使用Xiuno BBS定义好的函数是最好的方式。对于有经验的开发者而言,可以创建自己的数据表来存储数据。
引用《xiuno4.0开发手册》:
#### KV
- 优势:简单直接,持久存储,永不过期
- 缺点:每次调用`kv_get`就会读取一次数据库
```php
kv_get('键');
kv_set('键', '值');
kv_delete('键');
```
#### Cache 缓存
- 优势:高速,适合需要多次读取,并且对数据本身时效性要求低的数据
- 缺点:不能用于需要持久存储的数据,不建议用于对数据本身时效性要求高的数据
```php
cache_get('键');
cache_set('键', '值', 过期时间秒数); // 时机过期时间为“cache_set调用时的时间戳+过期时间秒数”;当缓存过期后,并不会重新刷新缓存,所以你需要自己实现
cache_delete('键');
```
##### 实现手动刷新缓存的方法:
```php
$cached_data = cache_get('键');
if (is_null($cached_data)) { // 如果cache_get返回null,则代表缓存过期
// 所以我们就不得不重新读取数据
// 这里的$cached_data是有意使用的,这样可以立即更新内容,刷掉cache_get的结果`null`
$cached_data = 数据库读取数据();
// 然后再次设置缓存
cache_set('键', $cached_data, 3600);
}
//然后照常使用$cached_data即可
```
#### KV+Cache 持久存储+缓存加速
- 优势:结合了以上两个的优点,无需手动设置缓存过期时间
- 缺点:无法手动设置缓存过期时间。
- 你可能会希望自己控制缓存过期时间(因为有些数据的时效性要求高(但也要快速加载),缓存时长就短),所以还是建议使用kv\_get+cache\_set+cache\_get来实现类似效果(见上文)
```php
kv_cache_get('键');
kv_cache_set('键', '值');
kv_cache_delete('键');
```
#### Setting 设置
这是设置项专用的函数。
- 优势:与KV+Cache一致,持久存储+缓存加速
- 缺点:无
```php
setting_get('键');
setting_set('键', '值');
setting_delete('键');
```
#### Runtime 统计
这是统计信息专用的函数。如主页的帖子数量、用户数量等。如果你也有这样的需求(例如统计访客人数),那么你可以使用这个函数。
- 优势:与KV+Cache一致,持久存储+缓存加速
- 缺点:无
```php
runtime_get('键');
runtime_set('键', '值');
runtime_delete('键');
```
但是我们通常只需使用KV、Cache、Setting这三种即可满足大多数需求,因为职责分明。
- KV:普通数据
- Cache:缓存
- Setting:插件设置
以及,以上所有的set函数的值都可以使用:
- string
- int
- float
- array(含多维数组)
这些类型。
#### 使用数据库表存储数据
这部分我建议你直接看`Xiuno BBS根目录/model`文件夹里各个文件的写法,不再赘述。这些文件都是最佳实践。
你将会在这些model文件里看到:
- 文件内部的分层
- 最原始的CRUD(无关联数据)
- 将db系列函数(如db\_insert、db\_find\_one等)包装成特化于业务的函数(如post\_\_create、post\_\_read等)
- 标准CRUD(有关联数据)
- 将以上post\_\_create、post\_\_read等再次包装成函数(如post\_create、post\_read等)
- 这些函数将会与thread、user产生关联,例如post\_create会同时变更thread表和user表的对应数据——比如创建帖子时,同时变更用户表里的积分等
- 将高频使用的函数(如post\_find)的一些高频使用场景再次包装成函数
- 如将“查找归属于某个帖子`TID`的回帖”包装成post\_find\_by\_tid
- 其他辅助函数
- 使用缓存机制来提高性能
- 例如post\_cache\_read
- **为什么有三个read函数?**
- `post__read` 是最基础的数据读取操作,专门用于直接从post表中读取数据,不涉及任何额外的处理或关联
- `post_read` 在上一个的基础上增加了从user表读取数据的功能,目的是在返回的帖子数据中包含发帖用户的详细信息。
- `post_cache_read` 进一步在上一个的基础上加入了缓存机制。这意味着当首次请求某个帖子的数据时,系统会先从缓存中查找,如果缓存中不存在,则从数据库中读取,并将结果存入缓存中以备后续请求使用。
这种方法能够显著提高频繁访问数据的加载速度,尤其是在高并发的情况下,可以大大减轻数据库的压力,提高系统的整体性能。
- 对外准备数据时,干掉一些敏感数据(如用户密码等)
- 例如post\_safe\_info
## 二、开发插件的准备
### 硬件
- 你,有学习能力就行
- 如果你认为你:
- 因为工作学习时间占据了自己时间的至少四分之三,留给自己的时间很少
- 觉得写代码像是爬一座很陡峭的山,离开了自己的舒适区,不想继续
- 不是很想学习,只是想用现成的插件
- 那也许本教程不适合你,你可以去找其他开发者有偿给你写一个。
- 一台电脑(台式电脑或笔记本电脑)
- 手机和平板电脑很难完成,但我理解学生党很有可能只有一部手机而没有电脑(甚至可能都没有使用电脑的经验)
- 互联网连接
### 软件
- 操作系统Windows、MacOS、Linux皆可
- 本地开发环境
- 电脑推荐使用XAMPP来搭建Apache+PHP+MySQL的环境
- 手机推荐KSWEB
- 当然,得在本地开发环境里安装好Xiuno BBS
- 文本编辑器(个人推荐Visual Studio Code)
- 在手机上可以使用QuickEdit App
- 浏览器
### 你要做什么插件
**在本教程中我们有个明确的目的:制作一个能展示自定义信息的独立页面,并在主页留出入口。**
但是明白自己要做什么插件,对你自己制作插件有很大的帮助。
分为两种情况:
- **你要为用户做一个满足用户需求的插件**
- 通过询问用户或者问卷调查等方式,了解目标用户的需求和痛点。
- 根据用户需求,规划插件的核心功能和辅助功能。
- **你自己想要做一个满足自己需求的插件**
- 详细列出你对这个插件的具体需求,比如需要实现哪些功能、解决哪些痛点、达到什么效果。
然后……
- 列出插件必须实现的核心功能点
- 基于上一点,绘制用户使用插件的流程图,确保每个步骤都符合用户直觉
- 这将会在开发插件的Model和Controller层很有帮助
- 与此同时你还需要考虑数据结构
- 例如数据库表的列怎么安排等
- 设计前端界面
- 最方便的方法就是使用Bootstrap(因为Xiuno BBS(和它的整个生态链)都是基于Bootstrap的)
- 可以去学习一下Bootstrap 4
- 当然你可能希望先从HTML、CSS和JS入手,因为它们是网页的基础。
- 考虑各种可能的异常情况(如数据不存在、用户输入会引发XSS攻击的内容等),并设计相应的错误处理机制
- 但好在Xiuno BBS的param函数可以免受XSS攻击
- 兼容性测试
- 虽然Xiuno BBS的最后一个官方版本是4.0.4,这个版本还最高兼容到PHP 7.2,但市面上有其他开发者维护的版本,它们普遍都能兼容到PHP 8.0+
- 而PHP 8比PHP 7更加严格,例如访问数组中不存在的键,PHP 8会直接报错(并立刻到那里结束),而不像PHP 7那样返回null(允许静默处理)。
- 严格的标准可以让写出更健壮的代码,而不会因为一些微小的疏忽而导致程序崩溃。
### 了解插件结构
一个典型的插件包含以下几个部分:
- hook // 【可选】容纳hook的具体实现——在特定的地方插入你的代码
- model // 【可选】插件自己的后台业务PHP代码 *(重用度高)*
- overwrite // 【可选】用于替换现有文件,只能替换使用`_include`函数引入的文件,如主题文件
- view
- htm
- 替换主题中具体的文件
- route // 【可选】插件自己的路由PHP代码 *(重用度低)*
- view // 【可选】插件自己的静态资源
- *(这个文件夹里的可以随心所欲使用,但通常有以下这些文件夹来归纳不同类型的文件)*
- css
- 容纳CSS文件
- 对应网址为`./plugin/插件文件夹/view/css/style.css`
- js
- 容纳JS文件
- 对应网址为`./plugin/插件文件夹/view/js/script.js`
- htm
- 容纳前台HTML文件;*话虽如此,实际上它是PHP文件,只是后缀名是htm*;因为Xiuno BBS并没有引入模板引擎,但是PHP自身的设计就是“能良好地与HTML”结合,所以你可以在html里写PHP作为轻量级模板引擎。
- 对应文件路径为`APP_PATH . 'plugin/插件文件夹/view/htm/page.htm'`
- 如`include _include(APP_PATH.'plugin/插件文件夹/view/htm/page.htm')`
- img
- 容纳图片文件
- conf.json // 插件的基本信息,如名称、版本等
- icon.png // 【可选】插件的图标
- install.php // 【可选】安装插件时执行的代码,通常用于初始化插件设置
- setting.php // 【可选】插件设置的后端逻辑
- setting.htm // 【可选】与setting.php对应的,插件设置的前端界面
- unstall.php // 【可选】卸载插件时执行的代码,通常用于删除插件设置
本次教程中要编写的插件,预计目录结构为:
- my\_page\_learn/
- hook/
-index\_route\_case\_end.php
- route/
- mypage.php
- view/
- htm/
- mypage.htm
- conf.json
- install.php
- setting.htm
- setting.php
- unstall.php
## 三、开始编写插件
打开Xiuno BBS根目录下的`index.php`文件,将`define('DEBUG', 0);`改为`define('DEBUG', 2);`。这样可以让你更快地看到开发中的错误信息。记得在开发完成后将其改回0。
### 创建文件夹
在Xiuno BBS根目录下的`plugin`文件夹中,创建名为`my_page_learn`的新文件夹。**这个文件夹的名字很重要,它会被用作插件的唯一标识。**
插件文件夹名称(下称**插件ID**)也有讲究,以“my\_page\_learn”为例:
- my:你自己的标识符,通常为插件作者名缩写,用于标记同一作者
- page:插件的“功能名称”,Xiuno BBS会利用这部分来实现插件互相卸载机制。
- 插件互相卸载机制:相同功能名称的插件的插件只会有一个被安装,其他相同功能名的插件都会被卸载。例如:
- xxx\_theme\_red
- yyy\_theme\_green
- zzz\_theme\_blue
- 将会只能同时安装一个,其他已经安装的插件会进行卸载。
- 但话虽如此,似乎只有当“功能名称”是theme时才有效。所以
- xxx\_post\_something
- yyy\_post\_something
- zzz\_post\_something
- 是可以同时安装的。
- learn:自由发挥区域,用于补充“功能名称”的描述
- **不能有第四个部分**,类似`my_page_learn_tutor`,但可以用驼峰命名法,如`my_page_learnTutor`
- 注意是大小写敏感的,需要维持插件中每个涉及到插件ID的地方拥有相同的大小写
- 如include文件时,需要写成`_include(APP_PATH.'plugin/my_page_learnTutor/view/htm/mypage.htm')`
### 创建`conf.json`
然后,进入my\_page\_learn文件夹,创建`conf.json`。这个文件定义了插件的基本信息,如插件名称、简介、版本号等。
创建了它,就可以在后台的插件页面里看到你的插件。
conf.json 文件内容详解(不要复制以下详解版,你需要写你自己的conf.json):
```jsonc
{
"name":"单页教程", // 插件名称
"brief":"插件介绍", // 插件介绍,支持HTML
"version":"1.0.0", // 插件版本号,必须遵守“语义化版本号规范” https://semver.org/lang/zh-CN/ 但仅限“主版本号+次版本号+修订号”的模式,不能有“先行版本号”、“版本编译信息”等部分
"bbs_version":"4.0.4", // 最低支持的Xiuno BBS版本,请写4.0.4,不要改变
"installed":0, // 是否安装Flag;手动更改0为1会让插件强制安装(反之亦然)
"enable":0, // 是否启用Flag;手动更改0为1会让插件强制启用(反之亦然)
"hooks_rank":[], // Hook调整顺序,请继续看详解
"overwrites_rank":[], // 替换文件调整顺序,请继续看详解
"dependencies":[] // 依赖插件列表,请继续看详解
}
```
#### hooks\_rank详解
当你发现你的hook没有出现在你希望的位置(如主页右侧小工具区域`index_site_brief_after.htm`,你的插件可能会排在其他插件之后出现,而你想要提前出现),请这样写:
```json
"hooks_rank":{
"index_site_brief_after.htm": -10
},
```
1. 将hooks\_rank的方括号换成花括号
2. 键是对应hook文件名
3. 值是提升或降低的幅度
- 负数是提前,正数是延后
- 应当以10为单位调整,但不是所有的插件都会遵守,所以能看到某些插件会设置成`-9999`,那这样的话你只能设置成更大的值了。
4. 这样的键值对可以有多个
#### overwrites\_rank详解
当你发现你希望要覆盖的文件没能成功覆盖时(如主题的导航菜单`header_nav.inc.htm`),请这样写:
```json
"overwrites_rank":{
"view/htm/header_nav.inc.htm": -10
},
```
1. 将overwrites\_rank的方括号换成花括号
2. 键是对应文件名(从你的插件文件夹里的overwrite文件夹开始的相对路径)
- 如`plugin/my_menu_custom/overwrite/view/htm/header_nav.inc.htm`,则为`view/htm/header_nav.inc.htm`
3. 值是提升或降低的幅度
- 负数是提前,正数是延后
- 应当以10为单位调整,但不是所有的插件都会遵守,所以能看到某些插件会设置成`-9999`,那这样的话你只能设置成更大的值了。
4. 这样的键值对可以有多个
#### dependencies详解
如果你的插件必须要与其他插件(如积分插件`tt_credits`)搭配使用才能让插件功能正常,请这样写:
```json
"dependencies":{
"tt_credits": "1.0.0"
},
```
1. 将dependencies的方括号换成花括号
2. 键是对应插件ID
3. 值是该插件的最低版本号(注意是字符串而不是数字!)
4. 这样的键值对可以有多个
### 注册路由
- 在`my_page_learn`文件夹里创建`hook`文件夹.
- 然后在`hook`文件夹里面创建`index_route_case_end.php`,内容如下:
```php
// 这个case对应param(0)的值,对应网址是mypage.htm
case 'mypage':
// 引入路由文件
include APP_PATH.'plugin/my_page_learn/route/mypage.php';
break;
```
这段代码告诉Xiuno BBS,当访问`mypage.htm`时,应该加载`mypage.php`文件。
### 创建路由文件
- 在`my_page_learn`文件夹里创建`route`文件夹。
- 然后进入`route`文件夹,创建`mypage.php`,内容如下:
```php
= $page_title ?>
```
这个文件定义了页面的内容和布局。
### 查看效果
回到浏览器,打开Xiuno BBS的后台管理页面,输入自己的密码进行登录。
然后进入“插件”部分,找到“单页教程”(my\_page\_learn)插件,点击“安装”。
然后在地址栏中输入`localhost/mypage.htm`。现在你应该能看到我们刚刚创建好的页面。
恭喜你初步完成一款插件的开发!
#### 然后呢?
你可能会觉得“每次要更新页面内容都要打开编辑器,好麻烦呀!”
那么我们就让这部分变成动态生成的!
### 让页面内容“动起来”
打开Xiuno BBS的后台管理页面,然后进入“插件”部分,找到“单页教程”(my\_page\_learn)插件,点击“卸载”。
然后回到文件管理器,在`my_page_learn`文件夹里创建这些文件:
- install.php
- unstall.php
- setting.php
- setting.htm
我们一个个看文件内容:
#### install.php
安装
```php
'我的页面标题', // 页面标题
'content'=>'我的页面内容' // 页面内容
];
// 保存设置
setting_set('my_page_learn_setting', $setting);
}
?>
```
#### unstall.php
卸载
```php
```
#### setting.php
设置-后端
```php
```
#### setting.htm
设置-前端
```php
我的页面设置
```
创建好以上文件后,进入`my_page_learn`文件夹里的`view`文件夹。
#### mypage.htm
然后进入`htm`文件夹,打开`mypage.htm`,更新这些地方:
```php
= $page_title ?>
```
打开Xiuno BBS的后台管理页面,然后进入“插件”部分,找到“单页教程”(my\_page\_learn)插件,点击“安装”,然后点击“单页教程”(my\_page\_learn)插件的“设置”。在插件设置页面中,随意更改页面标题和页面内容文本框内容。
然后在地址栏中输入`localhost/mypage.htm`。现在你应该能看到我们刚刚创建好的页面。
如果准确遵循步骤的话,页面内容应该会有所变化。
### 在主页添加入口
创建好的页面。如果只能让用户手动输入网址才能访问,那多麻烦!所以我们要使用Hook机制在主页添加按钮,来让用户点击按钮进入页面。
- 打开`my_page_learn`文件夹里的`hook`文件夹.
- 然后在`hook`文件夹里面创建`index_site_brief_after.htm`,内容如下:
```html
进入页面
```
这样,当用户浏览主页时,就能看到这个按钮,并通过点击它直接跳转到你创建的新页面了。
***
**恭喜你完成单页插件的开发,并掌握了基础的Xiuno BBS插件开发所需的知识点!**
## 四、然后呢?
你可以开始继续鼓捣以上我们刚刚创建的插件,尝试:
- 添加更多设置项
- 简单的复制粘贴就可以了
- 添加更多页面
- 尝试通过Route机制增加多个页面
- 提示:使用Switch case
- 用var\_dump输出全局变量`$user,$uid ,$gid ,$fid ,$tid`里的内容
- 然后使用if语句判断用户`uid`是否登录,并以此输出不同的内容
- 提示:如果没登录,uid=0
如果你在开发过程中遇到任何问题,欢迎在本站寻求帮助。